/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.web.servlet.tags.form;

import org.springframework.lang.Nullable;

import javax.servlet.jsp.JspException;
import java.util.Map;

/**
 * The {@code <input>} tag renders an HTML 'input' tag with type 'text' using
 * the bound value.
 *
 * <p>
 * <table>
 * <caption>Attribute Summary</caption>
 * <thead>
 * <tr>
 * <th class="colFirst">Attribute</th>
 * <th class="colOne">Required?</th>
 * <th class="colOne">Runtime Expression?</th>
 * <th class="colLast">Description</th>
 * </tr>
 * </thead>
 * <tbody>
 * <tr class="altColor">
 * <td><p>accesskey</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Standard Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>alt</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Optional Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>autocomplete</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>Common Optional Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>cssClass</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Optional Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>cssErrorClass</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Optional Attribute. Used when the bound field has errors.</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>cssStyle</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Optional Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>dir</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Standard Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>disabled</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Optional Attribute. Setting the value of this attribute to
 * 'true' will disable the HTML element.</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>htmlEscape</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>Enable/disable HTML escaping of rendered values.</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>id</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Standard Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>lang</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Standard Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>maxlength</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Optional Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>onblur</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>onchange</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>onclick</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>ondblclick</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>onfocus</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>onkeydown</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>onkeypress</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>onkeyup</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>onmousedown</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>onmousemove</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>onmouseout</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>onmouseover</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>onmouseup</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>onselect</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Event Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>path</p></td>
 * <td><p>true</p></td>
 * <td><p>true</p></td>
 * <td><p>Path to property for data binding</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>readonly</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Optional Attribute. Setting the value of this attribute to
 * 'true' will make the HTML element readonly.</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>size</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Optional Attribute</p></td>
 * </tr>
 * <tr class="rowColor">
 * <td><p>tabindex</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Standard Attribute</p></td>
 * </tr>
 * <tr class="altColor">
 * <td><p>title</p></td>
 * <td><p>false</p></td>
 * <td><p>true</p></td>
 * <td><p>HTML Standard Attribute</p></td>
 * </tr>
 * </tbody>
 * </table>
 *
 * @author Rob Harrop
 * @author Juergen Hoeller
 * @author Rossen Stoyanchev
 * @since 2.0
 */
@SuppressWarnings("serial")
public class InputTag extends AbstractHtmlInputElementTag {

    public static final String SIZE_ATTRIBUTE = "size";

    public static final String MAXLENGTH_ATTRIBUTE = "maxlength";

    public static final String ALT_ATTRIBUTE = "alt";

    public static final String ONSELECT_ATTRIBUTE = "onselect";

    public static final String AUTOCOMPLETE_ATTRIBUTE = "autocomplete";


    @Nullable
    private String size;

    @Nullable
    private String maxlength;

    @Nullable
    private String alt;

    @Nullable
    private String onselect;

    @Nullable
    private String autocomplete;

    /**
     * Get the value of the '{@code size}' attribute.
     */
    @Nullable
    protected String getSize() {
        return this.size;
    }

    /**
     * Set the value of the '{@code size}' attribute.
     * May be a runtime expression.
     */
    public void setSize(String size) {
        this.size = size;
    }

    /**
     * Get the value of the '{@code maxlength}' attribute.
     */
    @Nullable
    protected String getMaxlength() {
        return this.maxlength;
    }

    /**
     * Set the value of the '{@code maxlength}' attribute.
     * May be a runtime expression.
     */
    public void setMaxlength(String maxlength) {
        this.maxlength = maxlength;
    }

    /**
     * Get the value of the '{@code alt}' attribute.
     */
    @Nullable
    protected String getAlt() {
        return this.alt;
    }

    /**
     * Set the value of the '{@code alt}' attribute.
     * May be a runtime expression.
     */
    public void setAlt(String alt) {
        this.alt = alt;
    }

    /**
     * Get the value of the '{@code onselect}' attribute.
     */
    @Nullable
    protected String getOnselect() {
        return this.onselect;
    }

    /**
     * Set the value of the '{@code onselect}' attribute.
     * May be a runtime expression.
     */
    public void setOnselect(String onselect) {
        this.onselect = onselect;
    }

    /**
     * Get the value of the '{@code autocomplete}' attribute.
     */
    @Nullable
    protected String getAutocomplete() {
        return this.autocomplete;
    }

    /**
     * Set the value of the '{@code autocomplete}' attribute.
     * May be a runtime expression.
     */
    public void setAutocomplete(String autocomplete) {
        this.autocomplete = autocomplete;
    }

    /**
     * Writes the '{@code input}' tag to the supplied {@link TagWriter}.
     * Uses the value returned by {@link #getType()} to determine which
     * type of '{@code input}' element to render.
     */
    @Override
    protected int writeTagContent(TagWriter tagWriter) throws JspException {
        tagWriter.startTag("input");

        writeDefaultAttributes(tagWriter);
        Map<String, Object> attributes = getDynamicAttributes();
        if (attributes == null || !attributes.containsKey("type")) {
            tagWriter.writeAttribute("type", getType());
        }
        writeValue(tagWriter);

        // custom optional attributes
        writeOptionalAttribute(tagWriter, SIZE_ATTRIBUTE, getSize());
        writeOptionalAttribute(tagWriter, MAXLENGTH_ATTRIBUTE, getMaxlength());
        writeOptionalAttribute(tagWriter, ALT_ATTRIBUTE, getAlt());
        writeOptionalAttribute(tagWriter, ONSELECT_ATTRIBUTE, getOnselect());
        writeOptionalAttribute(tagWriter, AUTOCOMPLETE_ATTRIBUTE, getAutocomplete());

        tagWriter.endTag();
        return SKIP_BODY;
    }

    /**
     * Writes the '{@code value}' attribute to the supplied {@link TagWriter}.
     * Subclasses may choose to override this implementation to control exactly
     * when the value is written.
     */
    protected void writeValue(TagWriter tagWriter) throws JspException {
        String value = getDisplayString(getBoundValue(), getPropertyEditor());
        String type = null;
        Map<String, Object> attributes = getDynamicAttributes();
        if (attributes != null) {
            type = (String) attributes.get("type");
        }
        if (type == null) {
            type = getType();
        }
        tagWriter.writeAttribute("value", processFieldValue(getName(), value, type));
    }

    /**
     * Flags {@code type="checkbox"} and {@code type="radio"} as illegal
     * dynamic attributes.
     */
    @Override
    protected boolean isValidDynamicAttribute(String localName, Object value) {
        return !("type".equals(localName) && ("checkbox".equals(value) || "radio".equals(value)));
    }

    /**
     * Get the value of the '{@code type}' attribute. Subclasses
     * can override this to change the type of '{@code input}' element
     * rendered. Default value is '{@code text}'.
     */
    protected String getType() {
        return "text";
    }

}
